#include <iostream>
#include <cstdio>
using namespace std;

// 这里常考一道笔试题：sizeof(Baseone)是多少？
class Baseone
{
public:
	virtual void Func1()
	{
		cout << "Func1()" << endl;
	}

private:
	int _b = 1;
};

void test1()
{
	// 多了一个虚函数表指针，里面有虚函数的地址
	// 虚函数表就是一个函数指针数组，存的是虚函数的地址
	cout << sizeof(Baseone) << endl;
}

// 多态如何实现指向谁就调用谁的虚函数？
// 多态是运行时到指向的对象的虚表中查找要调用的虚函数的地址来进行调用

// 虚函数存在哪？ 代码段
// 虚函数表存在哪？ 代码段
// 同一个类型的不同对象虚函数地址是一样的，证明虚函数不是存在栈的
// 因为栈是一个一个栈帧，出站后地址会改变
// 误区：虚函数不是存在虚表，虚表存的是虚函数的地址
class Base
{
public:
	virtual void func1() { cout << "Base::func1" << endl; }
	virtual void func2() { cout << "Base::func2" << endl; }

private:
	int a;
};
class Derive : public Base
{
public:
	virtual void func1() { cout << "Derive::func1" << endl; }
	virtual void func3() { cout << "Derive::func3" << endl; }
	virtual void func4() { cout << "Derive::func4" << endl; }

private:
	int b;
};

void test2()
{
	// 取出对象的前四个字节
	Base b1;
	printf("vftptr虚函数表:%p\n", *(int *)&b1);

	int i = 0;
	int *p1 = &i;
	int *p2 = new int;
	const char *p3 = "hello";
	printf("栈变量:%p\n", p1);
	printf("堆变量:%p\n", p2);
	printf("代码段常量:%p\n", p3);
	printf("代码段虚函数地址:%p\n", &Base::func1);
}

void f(int i)
{
	cout << i << endl;
}
void f(double i)
{
	cout << i << endl;
}

void test3()
{
	int i = 0;
	double d = 1.1;
	// 静态绑定 静态的多态（静态：编译时确定函数地址）
	f(i);
	f(d);

	// 动态绑定 动态的多态（动态：运行时到虚表中找虚函数地址）
	Base *p = new Base;
	p->func1();
	p = new Derive;
	p->func1();
}

// 打印虚函数表
typedef void (*VF_PTR)(); // 函数指针类型typedef
void PrintVFTable(VF_PTR *pTable)
{
	for (size_t i = 0; pTable[i] != nullptr; i++)
	{
		printf("虚函数表[%d]:%p->", i, pTable[i]);
		VF_PTR f = pTable[i];
		f();
	}
}
void test4()
{
	Base b;
	Derive d;
	PrintVFTable(*(VF_PTR **)&b);
	PrintVFTable(*(VF_PTR **)&d);
}

class Base1
{
public:
	int _b1;
};
class Base2
{
public:
	int _b2;
};
class Derive1 : public Base1, public Base2
{
public:
	int _d;
};
void test5()
{
	//p1 = p3 != p2
	//子类赋值给父类会发生切片，所以p1和p3开始指向的是_b1，p2开始指向的是_b2
	//虽然p1和p3指向的地址一样，但是访问的大小不同
	Derive1 d;
	Base1 *p1 = &d;
	Base2 *p2 = &d;
	Derive1 *p3 = &d;
}

int main()
{
	// test1();
	// test2();
	// test3();
	test4();
	return 0;
}